package cn.sunxyz.idea.demo.proxy;

import cn.sunxyz.idea.demo.aspect.AdviceAspect;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * Created by yangrd on 2017/5/25.
 */
 class AdviceProxy implements Proxy {

    private Container<AdviceAspect> adviceAspectContainer = new Container<>();

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before(obj, method, args);
        Object result = null;
        try {
            result = proxy.invokeSuper(obj, args);
        } catch (Exception e) {
            exception(obj, method, args, e);
            throw e;
        }
        after(obj, method, args, result);
        return result;
    }

    public AdviceProxy addAdviceAspect(AdviceAspect adviceAspect) {
        adviceAspectContainer.add(adviceAspect);
        return this;
    }

    public AdviceProxy addAdviceAspectAll(Collection<? extends AdviceAspect> adviceAspects) {
        adviceAspectContainer.addAll(adviceAspects);
        return this;
    }

    private void before(Object obj, Method method, Object[] args) {
        Iterator<AdviceAspect> ascIterator = adviceAspectContainer.getASCIterator();
        while (ascIterator.hasNext()) {
            AdviceAspect adviceAspect = ascIterator.next();
            if (!adviceAspect.filter(obj, method, args)) {
                adviceAspect.before(obj, method, args);
            }
        }
    }

    private void after(Object obj, Method method, Object[] args, Object result) {
        Iterator<AdviceAspect> descIterator = adviceAspectContainer.getDESCIterator();
        while (descIterator.hasNext()) {
            AdviceAspect adviceAspect = descIterator.next();
            if (!adviceAspect.filter(obj, method, args)) {
                adviceAspect.after(obj, method, args, result);
            }
        }
    }

    private void exception(Object obj, Method method, Object[] args, Exception e) {
        Iterator<AdviceAspect> ascIterator = adviceAspectContainer.getASCIterator();
        while (ascIterator.hasNext()) {
            AdviceAspect adviceAspect = ascIterator.next();
            if (!adviceAspect.filter(obj, method, args)) {
                adviceAspect.exception(obj, method, args, e);
            }
        }
    }

    class Container<T> extends ArrayList<T> {

        private int currentStep = 0;

        public Container() {
        }

        public Container(int initialCapacity) {
            super(initialCapacity);
        }

        public Container(Collection<? extends T> c) {
            super(c);
        }

        public Iterator<T> getASCIterator() {
            currentStep = 0;
            return ASC;
        }

        public Iterator<T> getDESCIterator() {
            currentStep = size() - 1;
            return DESC;
        }

        private final Iterator<T> ASC = new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return currentStep < size();
            }

            @Override
            public T next() {
                return get(currentStep++);
            }
        };

        private final Iterator<T> DESC = new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return currentStep > -1;
            }

            @Override
            public T next() {
                return get(currentStep--);
            }
        };
    }
}
